/**
 * @file Domain.h
 * @author Michael Barriault
 * @version 0.3
 * @date 10-09-23
 * @project T3nsors
 */

#pragma once

#include <vector>
#include <T3nsors/Macros.h>

namespace T3 {
	/**
	 * The Domain class contains all information normally associated with the domain of numerical functions. It also contains information regarding coordinate mappings and the metric tensor function.
	 */
	class Domain {
	private:
		std::vector<int> O;
		std::vector<real> r;
		std::vector<real> D;
		std::vector<real> P;
	public:
		int N /**< Dimensionality of Domain*/;

		/**
		 * Domain constructor
		 *
		 * Empty constructor, does nothing.
		 */
		Domain(void);
		
		/**
		 * Domain constructor
		 *
		 * Primary constructor to create instances of Domain.
		 *
		 * @param int, dimensionality @f$[1,\infty)@f$
		 * @param int, number of points in dimension 1 @f$[0,\infty)@f$
		 * @param real, lower bound on dimension 1 @f$(-\infty, \infty-DOUBLE_{MIN})@f$
		 * @param real, upper bound on dimension 1 @f$(-\infty+DOUBLE_{MIN}, \infty)@f$
		 */		
		Domain(int,...);
		
		/**
		 * Return size of dimension @f$i@f$
		 *
		 * @param int, dimension index @f$i@f$
		 * @return int, sie of dimension
		 */		
		int operator()(int);
//		int operator()(void);
//		int upto(int);
		int upto(int A) {
			if ( A == -1 ) return 1;
			int T = 1;
			for ( int i=0; i<A+1; i++ ) T *= O[i];
			return T;
		}
		
		int operator()(void) {
			return upto(N-1);
		}
		
		/**
		 * Returns minimum value in direction @f$i@f$
		 * See also Domain::max(int)
		 *
		 * @param int, dimension index @f$i@f$
		 * @return real, minimum value
		 */
		real min(int);
		real min(void);

		/**
		 * Returns maximum value in direction @f$i@f$
		 * See also Domain::min(int)
		 *
		 * @param int, dimension index @f$i@f$
		 * @return real, maximum value
		 */
		real max(int);
		real max(void);

		/**
		 * Returns grid spacing in direction @f$i@f$
		 *
		 * @param int, dimension index @f$i@f$
		 * @return real, grid spacing
		 */
		real d(int);

		/**
		 * Returns grid spacing in direction @f$0@f$, which should be same for all directions
		 *
		 * @return real, grid spacing
		 */
		real d(void);

		/**
		 * Returns boundary type in direction @f$i@f$
		 * 
		 * @param int, dimension index @f$i@f$
		 * @return real, boundary type. (-1 -> periodic, 0 -> soft, 1-> hard)
		 */
		real& p(int);
	};
}

/**
 * Merges two Domains. In the future, this will be used for adaptive mesh refinement (or similar techniques) but for now, just returns a copy of the larger (and other chosen implementations mean that mixing anything but two equal-size Domains, or any Domain and a single-point Domain, will cause a crash. Sorry!
 *
 * @param Domain
 * @param Domain
 * @return Domain, merged Domain
 */
T3::Domain Merge(T3::Domain,T3::Domain);

/**
 * Force exit if two integers aren't the same.
 *
 * @param int
 * @param int
 */
void CheckDims(T3::Domain,T3::Domain);

/**
 * Force exit if two Domains aren't the same size.
 *
 * @param Domain
 * @param Domain
 */
void CheckDims(int,int);
